#include "lua_f_base.h"
#include <cutils/io_base.h>

#define IO_BASE_METATABLE_NAME "CUtils.IOBase.MT"

typedef struct lua_io_base {
	io_base_t *base;
	io_event_iterator_t it;
} lua_io_base_t;


#define check_lua_io_base(L) (lua_io_base_t *)luaL_checkudata(L, 1, IO_BASE_METATABLE_NAME)

static int lua_f_io_base_accept(lua_State *L)
{
	sock_t s;
	lua_io_base_t *self = check_lua_io_base(L);
	io_base_t *base = self->base;

	if (io_base_accept(base, &s) < 0)
		RETERR("io_base_accept fail");

	io_client_t *cli = io_client_new(&s, base);
	if (!cli)
		RETERR("io_client_new fail");

	lua_pushlightuserdata(L, cli);
	return 1;
}

static int lua_f_io_base_loop(lua_State *L)
{
	lua_io_base_t *self = check_lua_io_base(L);
	io_base_t *base = self->base;
	int timeout = luaL_optint(L, 2, -1);
	
	io_event_iterator_term(&self->it);
	int nums = io_base_loop(base, timeout);
	lua_pushinteger(L, nums);
	if (nums < 0) {
		lua_pushstring(L, "io_base_loop fail");
		return 2;
	}

	io_event_iterator_init(base, &self->it);
	return 1;
}

static int lua_f_io_base_next_event(lua_State *L)
{
	lua_io_base_t *self = check_lua_io_base(L);
	
	io_event_t *ev = io_events_next(&self->it);
	if (!ev) {
		lua_pushnil(L);
		return 1;
	}

	lua_pushinteger(L, ev->event);
	switch(ev->event) {
	case IO_EVENT_ACCEPT:
	case IO_EVENT_SIGNAL:
	case IO_EVENT_CTRL:
		return 1;

	case IO_EVENT_READ:
	case IO_EVENT_WRITE:
	case IO_EVENT_CLOSE:
	case IO_EVENT_ERROR:
		lua_pushlightuserdata(L, ev->ctx);
		return 2;
	
	case IO_EVENT_UNKNOW:
	default:
		return 1;
	}
}

static int lua_f_io_base_term(lua_State *L)
{
	lua_io_base_t *self = check_lua_io_base(L);
	io_base_t *base = self->base;

	io_base_destroy(&base);
	self->base = NULL;
	return 0;
}

/*
 * mt = {
 *	__gc = lua_f_io_base_term,
 *	__index = {
 *		term = lua_f_io_base_term,
 *		accept = lua_f_io_base_accept,
 *		loop = lua_f_io_base_loop,
 *		
 *	},
 *
 * };
 *
 * io_base = {
 *	new = lua_f_io_base_new,
 *	version = lua_f_io_base_version,
 * };
 *
 */
static const struct luaL_Reg lua_f_io_base_func[] = {
	{"term", lua_f_io_base_term},
	{"accept", lua_f_io_base_accept},
	{"loop", lua_f_io_base_loop},
	{"next_event", lua_f_io_base_next_event},
	{NULL, NULL},
};

static int lua_f_io_base_new(lua_State *L)
{
	io_base_t *base = NULL;
	io_base_conf_t conf;
	memset(&conf, 0, sizeof(conf));

	int nums = lua_gettop(L);
	if (nums < 3)
		RETERR("invalid args");

	const char *name = luaL_checkstring(L, 1);
	const char *ip = luaL_checkstring(L, 2);
	int port = luaL_checkinteger(L, 3);
	sigset_t mask;
	sigemptyset(&mask);

	int i;
	for (i = 4; i <= nums; ++i) {
		int sig = luaL_checkinteger(L, i);
		sigaddset(&mask, sig);
	}

	snprintf(conf.name, sizeof(conf.name), "%s", name);
	snprintf(conf.ip, sizeof(conf.ip), "%s", ip);
	conf.port = port;
	if (nums > 3)
		conf.mask = &mask;
	else
		conf.mask = NULL;

	base = io_base_new(&conf);
	if (!base)
		RETERR("io_base_new fail");

	lua_io_base_t *self = lua_newuserdata(L, sizeof(lua_io_base_t));
	if (!self)
		RETERR("lua_newuserdata fail");

	self->base = base;
	luaL_getmetatable(L, IO_BASE_METATABLE_NAME);
	lua_setmetatable(L, -2);
	return 1;
}

static int lua_f_io_base_version(lua_State *L)
{
	const char *ver = "IOBase V0.0.1";
	lua_pushstring(L, ver);
	return 1;
}

static const struct luaL_Reg lua_f_io_base_mod[] = {
	{"new", lua_f_io_base_new},
	{"version", lua_f_io_base_version},
	{NULL, NULL},
};

int luaopen_io_base(lua_State *L)
{
	luaL_newmetatable(L, IO_BASE_METATABLE_NAME);
	LTABLE_ADD_CFUNC(L, -1, "__gc", lua_f_io_base_term);
	lua_newtable(L);
	luaL_register(L, NULL, lua_f_io_base_func);
	lua_setfield(L, -2, "__index");
	lua_pop(L, 1);

	luaL_register(L, "io_base", lua_f_io_base_mod);

#define SETCONST(EVENT) \
    lua_pushnumber(L, EVENT); \
    lua_setfield(L, -2, #EVENT)

	SETCONST(IO_EVENT_ACCEPT);
	SETCONST(IO_EVENT_READ);
	SETCONST(IO_EVENT_WRITE);
	SETCONST(IO_EVENT_ERROR);
	SETCONST(IO_EVENT_CLOSE);
	SETCONST(IO_EVENT_CTRL);
	SETCONST(IO_EVENT_SIGNAL);
	SETCONST(IO_EVENT_UNKNOW);

	return 1;
}
